#include "mstring.h"
#include "ansi.h"

mx_size_t mx_strlen (const char* const s)
{
    mx_size_t i;
    for (i = 0; s[i]; i++) ;
    return i;
}

mx_size_t mx_strcmp (const char *p1, const char *p2)
{
	const unsigned char *s1 = (const unsigned char *) p1;
    const unsigned char *s2 = (const unsigned char *) p2;
    unsigned char c1, c2;

    do {
        c1 = (unsigned char) *s1++;
        c2 = (unsigned char) *s2++;
        if(c1 == '\0')
            return c1 - c2;
    } while (c1 == c2);

    return c1 - c2;
}

void mx_memset (mx_uint8_pt addr, mx_uint8_t value, mx_usize_t size)
{
    while (size-- != 0)
    {
        *addr++ = value;
    }
}

/**
 * delete before and after spaces
 * @param str: target string, it must be writeable
 * @return: trimed string address
 */
mx_string_pt mx_strtrim (mx_string_pt str)
{
    mx_size_t i, j = -1;
    mx_string_pt ret = str;
    for (i = 0; str[i] != '\0'; i++)
    {
        if (str[i] != ' ')
        {
            ret = &str[i];
            break;
        }
    }
    if (str[i] == '\0')
    {
        ret = &str[i];
    }
    else
    {
        for (i++; str[i] != '\0'; i++)
        {
            if (str[i] != ' ')
            {
                j = i;
            }
        }
        if (j++ > 0 && str[j] != '\0')
        {
            str[j] = '\0';
        }
    }
    return ret;
}

/**
 * split string by char
 * @param str: target string, it can be modify
 * @param c: the spcified char
 * @param arr: an array of split strings
 * @return: strings array count
 */
mx_size_t mx_strsplitc (char* str, char c, char** arr)
{
    mx_size_t len = mx_strlen(str);
    mx_size_t arrlen = 1;
    if (arr == mx_null)
    {
        for (mx_size_t i = 0; i < len; i++)
        {
            if (str[i] == c)
            {
                arrlen ++;
            }
        }
    }
    else
    {
        arr[0] = str;
        for (mx_size_t i = 0; i < len; i++)
        {
            if (str[i] == c)
            {
                str[i] = '\0';
                arr[arrlen++] = &str[i + 1];
            }
        }
    }
    return arrlen;
}

/**
 * split string by char and discard the empty string
 * @param str: target string, it must be writeable
 * @param c: the spcified char
 * @param arr: an array of split strings, it can be null
 * @note: this function will modify the `str`, so that `str` must be writeable
 * @return: strings array count
 */
mx_size_t mx_strsplitc_trim (char* str, char c, char** arr)
{
    mx_size_t i;
    mx_size_t arrlen = 0;
    mx_bool_t isconti = mx_true;    // is continuous
    mx_size_t strlen = mx_strlen(str);
    /* input string is empty */
    if (strlen == 0)
    {
        arrlen = 0;
    }
    else
    {
        /* only calculate the number of strings after split */
        if (arr == mx_null)
        {
            for (i = 0; i < strlen; i++)
            {
                if (str[i] == c)
                {
                    isconti = mx_true;
                }
                else
                {
                    if (isconti == mx_true)
                    {
                        isconti = mx_false;
                        arrlen ++;
                    }
                }
            }
        }
        else
        {
            for (i = 0; i < strlen; i++)
            {
                if (str[i] == c)
                {
                    str[i] = '\0';
                    isconti = mx_true;
                }
                else
                {
                    if (isconti == mx_true)
                    {
                        isconti = mx_false;
                        arr[arrlen++] = &str[i];
                    }
                }
            }
        }
    }
    return arrlen;
}

mx_bool_t mx_strisempt (char* str)
{
    return (str[0] == '\0');
}

mx_void_pt mx_memcpy (mx_uint8_pt dest, mx_uint8_pt src, mx_usize_t count)
{
    mx_void_pt ret = (mx_void_pt)dest;

    if (dest == src)
    {
        return mx_null;
    }

    while (count--)
    {
        *dest++ = *src++;
    }
    
    return ret;
}

mx_string_pt mx_strcpy (mx_string_pt dst, mx_string_pt src)
{
    mx_string_pt _dst = dst;
    while (*_dst++ = *src++);
    return dst;
}

char* mx_strncpy (char* dst, const char* src, mx_size_t n)
{
    char *st = dst;
    while (n && (*dst++ = *src++))
    {
        n--;
    }
    if (n)
    {
        while (--n)
        {
            *dst++ = '\0';
        }
    }
    return st;
}

/* char number */
mx_size_t mx_strcn (mx_string_pt str, char c)
{
    mx_size_t ret = 0;
    mx_size_t i = 0;
    while (str[i] != '\0')
    {
        if (str[i] == c)
        {
            ret ++;
        }
        i ++;
    }
    return ret;
}

/**
 * get the specified char's index in string (left to right (forword))
 * @param str: target string
 * @param c: specified char
 * @return: the char's index, -1 means that not match
 */
mx_size_t mx_strci (mx_string_pt str, char c)
{
    mx_size_t ret = -1;
    mx_size_t i = 0;
    while (str[i] != '\0')
    {
        if (str[i] == c)
        {
            ret = i;
            break;
        }
        i ++;
    }
    return ret;
}

/* get the specified char's index in string (right to left (back)) */
mx_size_t mx_strcib (mx_string_pt str, char c)
{
    mx_size_t ret = -1;
    mx_size_t i = 0;
    while (str[i] != '\0')
    {
        if (str[i] == c)
        {
            ret = i;
        }
        i ++;
    }
    return ret;
}

/* get the specified char's index in string with number */
mx_size_t mx_strciwn (mx_string_pt str, char c, mx_size_t num)
{
    mx_size_t cnt = 1;
    mx_size_t ret = -1;
    mx_str_foreachi(str, pc, i, {
        if (str[i] == c)
        {
            if (cnt == num)
            {
                ret = i;
                break;
            }
            else
            {
                cnt ++;
            }
        }
    });
    return ret;
}

// mx_size_t mx_strsn (mx_string_pt str, mx_string_pt s)
// {
//     mx_size_t ret = 0;
//     mx_str_foreach(str, pc, {
//         if (*pc == s[0])
//         {
//             mx_str_foreach((pc + 1), pcn, {

//             })
//         }
//     })
// }

mx_size_t mx_strsi (mx_string_pt str, mx_string_pt s)
{
    mx_size_t ret = -1;
    mx_str_foreachi(str, pc, i, {
        mx_bool_t ismatch = mx_true;
        mx_str_foreachi(s, ipc, ii, {
            if (*ipc != pc[ii])
            {
                ismatch = mx_false;
                break;
            }
        });
        if (ismatch)
        {
            ret = i;
            break;
        }
    });
    return ret;
}

mx_size_t mx_strsib (mx_string_pt str, mx_string_pt s)
{
    mx_size_t ret = -1;
    mx_size_t strlen = mx_strlen(str);
    mx_size_t slen = mx_strlen(s);
    mx_size_t sleni = slen - 1;
    for (mx_size_t i = strlen - 1; i > -1; i--)
    {
        mx_bool_t ismatch = mx_true;
        for (mx_size_t j = sleni; j > -1; j--)
        {
            mx_size_t idx = i - (sleni - j);
            if (idx > -1 && s[j] != str[idx])
            {
                ismatch = mx_false;
                break;
            }
        }
        if (ismatch)
        {
            ret = i - sleni;
            break;
        }
    }
    return ret;
}

mx_bool_t mx_strisdigit (mx_string_pt str)
{
    if (*str != '\0' && (*str == '-' || *str == '+'))
        str ++;
    if (!ANSI_ISNUMBER(*str))
        return mx_false;
    mx_bool_t ret = mx_true;
    mx_str_foreach(str + 1, pc, {
        if (!ANSI_ISNUMBER(*pc))
        {
            ret = mx_false;
            break;
        }
    });
    return ret;
}

mx_int32_t mx_strtoi (mx_string_pt str)
{
    mx_int32_t ret = 0;
    mx_size_t i = 0;
    mx_bool_t isminus = mx_false;
    if (str[0] == '-')
    {
        isminus = mx_true;
        i = 1;
    }
    while (str[i] != '\0')
    {
        ret = ret * 10 + (str[i] - '0');
        i ++;
    }
    if (isminus)
    {
        ret = -ret;
    }
    return ret;
}

mx_bool_t mx_strendwithc (mx_string_pt str, char c)
{
    mx_size_t len = mx_strlen(str);
    return (str[len - 1] == c);
}

mx_bool_t mx_strstartwiths(mx_string_pt str, mx_string_pt s)
{
    mx_bool_t ret = mx_true;
    mx_str_foreachi(s, pc, i, {
        if (*pc != str[i])
        {
            ret = mx_false;
            break;
        }
    });
    return ret;
}

/**
 * replace chars
 * @param str: the target string
 * @param f: from
 * @param t: to
 * @return: void
 */
void mx_strreplacec (mx_string_pt str, char f, char t)
{
    mx_str_foreach(str, pc, {
        if (*pc == f)
            *pc = t;
    });
}
